package goutl

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"gitee.com/xiakai001/goutl/errutl"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

var (
	// DealLineTrimSpace 清除前后空格
	DealLineTrimSpace = strings.TrimSpace
	// DealLineTrim 清除前后指定字符
	DealLineTrim = _DealLineTrim
	// DealLineSplit 字符串分割，生成字符串数组
	DealLineSplit = _DealLineSplit
	// DealLineParseInt 转化为int
	DealLineParseInt = ParseStrToInt[int64]
	// DealLineParseFloat 转换为float
	DealLineParseFloat = ParseStrToFloat[float64]

	// SkipLineEmpty 跳过空字符串
	SkipLineEmpty = _SkipLineEmpty
	// SkipLineSpace 跳过空白字符串(包括空格、制表等)
	SkipLineSpace = _SkipLineSpace
)

func ReadTopLineFromFile[V any](filePath string, top int, skip func(string) bool, deal func(string) V, fn func(index int, line V)) error {
	return ReadTopLineFromFileWithErr(filePath, top, skip, deal, func(index int, line V) error {
		fn(index, line)
		return nil
	})
}

func ReadTopLineFromFileWithErr[V any](filePath string, top int, skip func(string) bool, deal func(string) V, fn func(index int, line V) error) error {
	filePath = GetAbsolutePath(filePath)

	// 创建句柄
	fi, err := os.Open(filePath)
	if err != nil {
		return errutl.Wrap(err)
	}

	err = readLinesFromReader(fi, top, skip, deal, fn)
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

// ReadLineFromFile 从文件中按行遍历文本
func ReadLineFromFile[V any](filePath string, skip func(string) bool, deal func(string) V, fn func(index int, line V)) error {
	return ReadTopLineFromFile(filePath, 0, skip, deal, fn)
}

// ReadLineFromFileWithErr 从文件中按行遍历文本，快速失败
func ReadLineFromFileWithErr[V any](filePath string, skip func(string) bool, deal func(string) V, fn func(index int, line V) error) error {
	return ReadTopLineFromFileWithErr(filePath, 0, skip, deal, fn)
}

// GetStringFromFile 获取文件内容
func GetStringFromFile(filePath string) (string, error) {
	filePath = GetAbsolutePath(filePath)

	// 创建句柄
	fi, err := os.Open(filePath)
	if err != nil {
		return "", errutl.Wrap(err)
	}
	defer func() { _ = fi.Close() }()

	str, err := getStringFromReader(fi)
	if err != nil {
		return "", errutl.Wrap(err)
	}

	return str, nil
}

// GetFromFile 获取文件内容
func GetFromFile(filePath string) ([]byte, error) {
	filePath = GetAbsolutePath(filePath)

	// 创建句柄
	fi, err := os.Open(filePath)
	if errutl.Cause(err) == syscall.ENOENT {
		return nil, nil
	}
	if err != nil {
		return nil, errutl.Wrap(err)
	}
	defer func() { _ = fi.Close() }()

	bb, err := getBytesFromReader(fi)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return bb, nil
}

func GetTopLinesFromFile[V any](filePath string, top int, skip func(string) bool, deal func(string) V) ([]V, error) {
	lines := make([]V, 0, 4)
	err := ReadTopLineFromFile(filePath, top, skip, deal, ReadLineParseToArr(&lines))
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return lines, nil
}

// GetLinesFromFile 按行获取文件内容
func GetLinesFromFile[V any](filePath string, skip func(string) bool, deal func(string) V) ([]V, error) {

	lines := make([]V, 0, 100)
	err := ReadLineFromFile(filePath, skip, deal, ReadLineParseToArr(&lines))
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return lines, nil
}

// GetCsvFromFile 获取csv格式文件内容
func GetCsvFromFile(filePath string) ([][]string, error) {

	filePath = GetAbsolutePath(filePath)

	// 创建句柄
	file, err := os.Open(filePath)
	if err != nil {
		return nil, errutl.Wrap(err)
	}
	defer func() {
		err := file.Close()
		errutl.CheckErr(err)
	}()

	csvReader := csv.NewReader(file)
	matrix, err := csvReader.ReadAll()
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return matrix, nil
}

// GetCsvWithTitleFromFile 获取csv格式文件内容
func GetCsvWithTitleFromFile(filePath string) (*Csv, error) {
	data, err := GetCsvFromFile(filePath)
	if err != nil {
		return nil, errutl.Wrap(err)
	}
	if len(data) == 0 {
		return &Csv{}, nil
	}
	titleMap := make(map[string]int, len(data[0]))
	for i, t := range data[0] {
		titleMap[t] = i
	}

	return &Csv{
		data:     data,
		titleMap: titleMap,
	}, nil
}

func GetMatrixFromFile(filePath string, sep string) ([][]string, error) {

	matrix, err := GetLinesFromFile(filePath, nil, DealLineSplit(sep))
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return matrix, nil
}

// WriteLinesToFile 按行写文件
func WriteLinesToFile(filePath string, lines []string) error {

	str := strings.Join(lines, "\n")
	err := WriteBytesToFile(filePath, []byte(str))
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

// WriteLineToFile 按行写文件
func WriteLineToFile(filePath string, line ...string) error {
	return WriteLinesToFile(filePath, line)
}

func WriteBytesToFile(filePath string, data []byte) error {
	writer, err := NewFileWriter(filePath, false)
	if err != nil {
		return errutl.Wrap(err)
	}
	defer func() {
		err := writer.Close()
		errutl.CheckErr(err)
	}()

	err = writer.WriteBytes(data)
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

type FileWriter struct {
	file    *os.File
	isEmpty bool
}

// Write 接前文，多个str空格隔开
func (f *FileWriter) Write(str ...string) error {
	return f.writeStrList(str, "")
}

func (f *FileWriter) writeStrList(arr []string, end string) error {

	str := strings.Join(arr, " ") + end
	_, err := f.file.WriteString(str)
	if err != nil {
		return errutl.Wrap(err)
	}

	if f.isEmpty && len(str) != 0 {
		f.isEmpty = false
	}

	return nil
}

// Writeln 接前文，多个str空格隔开，并且结尾会换行
func (f *FileWriter) Writeln(str ...string) error {

	err := f.writeStrList(str, "\n")
	if err != nil {
		return err
	}

	return nil
}

// WriteLine 另起一行（开头除外）,保证每个line都是单独的一行
func (f *FileWriter) WriteLine(line ...string) error {
	return f.WriteLines(line)
}

// WriteLines 另起一行（开头除外）,保证每个line都是单独的一行
func (f *FileWriter) WriteLines(lines []string) error {

	if len(lines) == 0 {
		return nil
	}

	var str string
	if f.isEmpty {
		str = strings.Join(lines, "\n")
	} else {
		str = "\n" + strings.Join(lines, "\n")
	}

	_, err := f.file.WriteString(str)
	if err != nil {
		return errutl.Wrap(err)
	}

	if f.isEmpty && len(str) != 0 {
		f.isEmpty = false
	}

	return nil
}

func (f *FileWriter) WriteBytes(bs []byte) error {
	_, err := f.file.Write(bs)
	if err != nil {
		return errutl.Wrap(err)
	}

	if f.isEmpty && len(bs) != 0 {
		f.isEmpty = false
	}
	return nil
}

func (f *FileWriter) Close() error {
	err := f.file.Close()
	if err != nil {
		return errutl.Wrap(err)
	}
	return nil
}

func NewFileWriter(filePath string, append bool) (*FileWriter, error) {

	filePath = GetAbsolutePath(filePath)

	dir := filepath.Dir(filePath)
	err := os.MkdirAll(dir, os.ModePerm)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	wFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	isEmpty := true
	if append {
		ret, err := wFile.Seek(0, io.SeekEnd)
		if err != nil {
			return nil, errutl.Wrap(err)
		}
		if ret != 0 {
			isEmpty = false
		}
	} else {
		err = wFile.Truncate(0)
		if err != nil {
			return nil, errutl.Wrap(err)
		}
	}

	return &FileWriter{
		file:    wFile,
		isEmpty: isEmpty,
	}, nil
}

func NewScannerFromFile(filePath string) (*Scanner, error) {
	filePath = GetAbsolutePath(filePath)

	// 创建句柄
	fi, err := os.Open(filePath)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return NewScanner(fi), nil
}

func rotateMatrix(matrix [][]string) [][]string {
	w := 0
	for _, row := range matrix {
		if len(row) > w {
			w = len(row)
		}
	}

	rMatrix := make([][]string, w)
	if w == 0 {
		return nil
	}

	i := len(matrix) - 1
	for j := 0; j < w; j++ {
		for ; i >= 0; i-- {
			if len(matrix[i]) > j {
				rMatrix[j] = make([]string, i+1)
				break
			}
		}
	}

	for i, row := range matrix {
		for j, a := range row {
			rMatrix[j][i] = a
		}
	}

	return rMatrix
}

func buildLines(content [][]string, sep string, byRow bool, fillSep bool) []string {

	if !byRow {
		content = rotateMatrix(content)
	}

	maxL := 0
	for _, c := range content {
		if len(c) > maxL {
			maxL = len(c)
		}
	}

	lines := make([]string, 0, len(content))
	if !fillSep {
		for _, c := range content {
			lines = append(lines, strings.Join(c, sep))
		}
		return lines
	}

	for _, c := range content {
		if len(c) == 0 {
			lines = append(lines, RepeatString(sep, maxL-1))
		} else {
			lines = append(lines, strings.Join(c, sep)+RepeatString(sep, maxL-len(c)))
		}
	}

	return lines
}

// WriteMatrixToFile 二维表到文件
func WriteMatrixToFile(filePath string, content [][]string, sep string, byRow bool, fillSep bool) error {
	lines := buildLines(content, sep, byRow, fillSep)

	err := WriteLinesToFile(filePath, lines)
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

// WriteCsvToFile 写csv格式文件
func WriteCsvToFile(filePath string, content [][]string) error {

	var bb bytes.Buffer
	csvWriter := csv.NewWriter(&bb)

	err := csvWriter.WriteAll(content)
	if err != nil {
		return errutl.Wrap(err)
	}
	csvWriter.Flush()

	str := bb.String()
	if str[len(str)-1] == '\n' {
		str = str[0 : len(str)-1]
	}

	err = WriteLineToFile(filePath, str)
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

// GetAbsolutePath 获取绝对路径
func GetAbsolutePath(filePath string) string {
	filePath = strings.TrimSpace(filePath)

	if len(filePath) == 0 {
		filePath, err := filepath.Abs("")
		errutl.CheckErr(err)
		return filePath
	}

	if filePath[0] == '/' {
		return filePath
	}

	filePath, err := filepath.Abs(filePath)
	errutl.CheckErr(err)
	return filePath
}

// GetProjectRoot 获取go项目根目录的绝对路径
func GetProjectRoot() string {

	curPath, err := filepath.Abs("")
	errutl.CheckErr(err)

	for len(curPath) > 1 {
		_, err := os.Stat(filepath.Join(curPath, "go.mod"))
		//当为空文件或文件夹存在
		if err == nil {
			return curPath
		}
		curPath = path.Dir(curPath)
	}
	return ""
}

func RepeatString(str string, count int) string {
	b := make([]byte, 0, len(str)*count)
	for i := 0; i < count; i++ {
		b = append(b, []byte(str)...)
	}

	return string(b)
}

func _DealLineTrim(sep string) func(string) string {
	return func(s string) string {
		return strings.Trim(s, sep)
	}
}

func _DealLineSplit(sep string) func(string) []string {
	return func(s string) []string {
		return Split(s, sep)
	}
}

func _SkipLineEmpty(s string) bool {
	return len(s) == 0
}

func _SkipLineSpace(s string) bool {
	return len(strings.TrimSpace(s)) == 0
}

func ReadLineParseToArr[V any](arr *[]V) func(i int, line V) {
	return func(i int, line V) {
		*arr = append(*arr, line)
	}
}

func LoadJsonFromFile[V any](filePath string) (V, error) {
	bb, err := GetFromFile(filePath)

	v := Zero[V]()

	if err != nil {
		return v, errutl.Wrap(err)
	}
	if len(bb) == 0 {
		return v, nil
	}

	v, err = UnMarshal[V](string(bb))
	if err != nil {
		return v, errutl.Wrap(err)
	}

	return v, nil
}

func FileExist(path string) (bool, error) {

	path = GetAbsolutePath(path)

	// 使用os.Stat来获取文件信息
	_, err := os.Stat(path)

	// 检查文件是否存在
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, errutl.Wrap(err)
}

type Csv struct {
	data     [][]string
	titleMap map[string]int
}

type CsvRow struct {
	data     []string
	titleMap map[string]int
}

func (c *Csv) Len() int {
	return len(c.data) - 1
}

func (c *Csv) Data() [][]string {
	return c.data
}

func (c *Csv) Foreach(fn func(index int, row *CsvRow)) {
	for i := 0; i < len(c.data)-1; i++ {
		fn(i, c.GetRow(i))
	}
}

func (c *Csv) Get(index int, title string) string {
	titleIndex, ok := c.titleMap[title]
	if !ok {
		panic(fmt.Sprintf("title(%s) is not exist", title))
	}
	return c.data[index+1][titleIndex]
}

func (c *Csv) GetInt64(index int, title string) int64 {
	return ParseStrToInt[int64](c.Get(index, title))
}

func (c *Csv) GetRow(index int) *CsvRow {
	return &CsvRow{
		data:     c.data[index+1],
		titleMap: c.titleMap,
	}
}

func (r *CsvRow) Data() []string {
	return r.data
}

func (r *CsvRow) Get(title string) string {
	titleIndex, ok := r.titleMap[title]
	if !ok {
		panic(fmt.Sprintf("title(%s) is not exist", title))
	}
	return r.data[titleIndex]
}

func (r *CsvRow) GetInt64(title string) int64 {
	return ParseStrToInt[int64](r.Get(title))
}
